
/****************************************************************************
**
** Copyright (C) 2004-2008 VATATA.com . All rights reserved.
**
** This file is the part of the Miniutil Poject of VATATA.com.
** Vatata's miniutil library is lightweight and portable (for linux
** and Windows). The C++ Class Library included Threadpool, Dll danymic loading, 
** General socket application model and tcp server, httpserver/servlet, etc. 
** It is a pratical utility disigned for who don't want learn the Big C++ 
** Developing Platform, such as  Boost or ACE.
**
** This file may be used under the terms of the GNU General Public
** License versions 2.0 or 3.0 as published by the Free Software
** Foundation and appearing in the files LICENSE.GPL2 and LICENSE.GPL3
** included in the packaging of this file.  Alternatively you may (at
** your option) use any later version of the GNU General Public
** License if such license has been publicly approved by VATATA.com.
** (or its successors, if any) and the Miniutil Project Aministrators. 
**
** Please review the following information to ensure GNU General
** Public Licensing requirements will be met:
** http://www.gnu.org/licenses/lgpl.html. If you are unsure which 
** license is appropriate for your use, please review the following 
** information: http://code.google.com/p/miniutil/ or contact 
** http://www.vatata.com.
**
** In addition, as a special exception, VATATA.com, as the sole
** copyright holder for Miniutil Project, grants users of VATATA
** P2P Platform the right to release and package with the related 
** libraries without the source code.
**
** This file is provided "AS IS" with NO WARRANTY OF ANY KIND,
** INCLUDING THE WARRANTIES OF DESIGN, MERCHANTABILITY AND FITNESS FOR
** A PARTICULAR PURPOSE. vatata reserves all rights not expressly
** granted herein.
**
** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE
** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
**
****************************************************************************/


/*
	$Id: exceptioncapture_w32.cpp 389 2009-12-02 02:37:08Z liu.lie $

	Capture Win32 exception and avoid the Error messagebox to appear
	Usage:

	Generate some ExceptionHandlerBasic instance, and call , such as
		ExceptionCapture::GetInstance()->AddExceptionHandler(new CreateDumpFile("aaa.dmp"));

	then in the code you want to capture exception:

	__try
	{
		... code
	}
	__except((*ExceptionCapture::GetInstance())(GetExceptionInformation()), EXCEPTION_EXECUTE_HANDLER)
	{
		...other thing you want to do, but usually, you have do what you want in ExceptionHandlerBasic
	}

	SAMPLE:

	int test()
	{
		char *p = 0x00;
		__try
		{
			*p = 5;
		}
		__except((*ExceptionCapture::GetInstance())(GetExceptionInformation()), EXCEPTION_EXECUTE_HANDLER)
		{
			//suffix();
		}

		return 0;
	}

	Noti: __try can not used in function has any object created and destoried including temp object!
*/

#ifdef WIN32

#include "exceptioncapture_w32.h"

#include <io.h>
//#include "psapi.h"
#include "DbgHelp.h"

namespace miniutil
{
	std::string CreateDumpFile::filename = "dumpfile.dmp";
	LONG WINAPI CreateDumpFile::DebugMiniDumpFilter(PEXCEPTION_POINTERS ppoint)
	{
	    MINIDUMP_EXCEPTION_INFORMATION minfo;
	    minfo.ThreadId = GetCurrentThreadId();
	    minfo.ExceptionPointers = ppoint;
	    minfo.ClientPointers = TRUE;

	    HANDLE hFile = CreateFile(TEXT(filename.c_str()),     // file to create
					       GENERIC_WRITE,          // open for writing
					       0,                      // do not share
					       NULL,                   // default security
					       CREATE_ALWAYS,          // overwrite existing
					       FILE_ATTRIBUTE_NORMAL, // normal file
					       NULL);                  // no attr. template

	    if(hFile != INVALID_HANDLE_VALUE)
	    {
            static BOOL (WINAPI *p_MiniDumpWriteDump)(HANDLE, DWORD, HANDLE, MINIDUMP_TYPE, PMINIDUMP_EXCEPTION_INFORMATION, LPVOID, LPVOID)=NULL;
	        if( !p_MiniDumpWriteDump )
            {
		        HMODULE h=GetModuleHandle("dbghelp.dll");
		        if(!h)	h=LoadLibrary("dbghelp.dll");
		        if(!h)	return -1;
		        p_MiniDumpWriteDump=(BOOL (WINAPI *)(HANDLE, DWORD, HANDLE, MINIDUMP_TYPE, PMINIDUMP_EXCEPTION_INFORMATION, LPVOID, LPVOID))GetProcAddress(h,"MiniDumpWriteDump");
		        if(! p_MiniDumpWriteDump)	return -1;
	        }

            //MINIDUMP_TYPE MiniDumpWithDataSegs = MiniDumpNormal   
            //    | MiniDumpWithHandleData   
            //    | MiniDumpWithUnloadedModules   
            //    | MiniDumpWithIndirectlyReferencedMemory   
            //    | MiniDumpScanMemory   
            //    | MiniDumpWithProcessThreadData;   
                //| MiniDumpWithThreadInfo;  
                //MiniDumpWithFullMemory

            int ret = p_MiniDumpWriteDump(
                        GetCurrentProcess(),
                        GetCurrentProcessId(),
                        hFile,
                        MiniDumpNormal, 
                        &minfo,
                        NULL,
                        NULL);
		    CloseHandle(hFile);
	    }
	    return EXCEPTION_EXECUTE_HANDLER;
	}

    CreateDumpFile::CreateDumpFile(std::string fi)
    {
	    filename = fi;
    }

    int CreateDumpFile::operator()(PEXCEPTION_POINTERS ppoint)
    {
		return DebugMiniDumpFilter(ppoint);
    }

    //-----------------------------------------------

    ExceptionCapture* ExceptionCapture::aInstance = NULL;

    ExceptionCapture* ExceptionCapture::GetInstance()
    {
	    if(aInstance == NULL)
		    aInstance = new ExceptionCapture();
	    return aInstance;
    }

    int ExceptionCapture::CatchUnhandleException(const std::string& name)
    {
        if(name == "dumpfile")
            ::SetUnhandledExceptionFilter(CreateDumpFile::DebugMiniDumpFilter);
        else
            ::SetUnhandledExceptionFilter(CreateDumpFile::DebugMiniDumpFilter);
        return 0;
    }

    static  string & excp_GetModuleBaseName()
    {
        char buf[MAX_PATH+1];
        DWORD size = ::GetModuleFileName( NULL,  buf, MAX_PATH );

        for( unsigned int i=0; i< size; i++ )
	        if ( buf[i] == '\\' ) buf[i] = '/';

        buf[size] = 0;
        std::string modulename = buf;

        static string baseName;

        if ( baseName.length() > 0 )
	        return baseName;

        string & exeName = modulename;

        string::size_type lastdot = exeName.find_last_of( "." );
        if ( lastdot == string::npos )
        {
            baseName = exeName;
            return baseName;
        }

        string::size_type lastsep = exeName.find_last_of( "\\/" );

        if ( lastsep != string::npos && lastsep > lastdot )
        {
            baseName = exeName;
            return baseName;
        }

        baseName = exeName.substr(0, lastdot );
        return baseName;
    }

    int ExceptionCapture::AddExceptionHandler(const std::string& name, ExceptionHandlerBasic* punit)
    {
        miniutil::auto_lock kk(&mtx);
        if(vcallbacks.find(name) == vcallbacks.end())
        {
            if(punit == NULL)
            {
                if(name == "dumpfile")
                {
                    //char buf[MAX_PATH+1];
                    //DWORD size = ::GetModuleBaseNameA(NULL, NULL, buf, MAX_PATH);
                    //buf[size] = 0x00;
                    //std::string dump_file = buf;
                    std::string dump_file = excp_GetModuleBaseName();
                    dump_file += ".dmp";
                    punit = new CreateDumpFile( dump_file );
                }
                else //zip/mail
                {
                }
            }
            if(punit != NULL)
            {
                vcallbacks.insert(std::make_pair(name, punit));
                return 0;
            }
        }
	    return -1;
    }

    int ExceptionCapture::ClearExceptionHandler(const std::string& name)
    {
        miniutil::auto_lock kk(&mtx);
        if(vcallbacks.find(name) != vcallbacks.end())
        {
            delete vcallbacks[name];
            vcallbacks.erase(name);
        }
        return 0;
    }

    ExceptionCapture::ExceptionCapture()
    {
    }

    ExceptionCapture::~ExceptionCapture()
    {
        std::map<std::string, ExceptionHandlerBasic*>::iterator iter_se;
        for(iter_se = vcallbacks.begin(); iter_se != vcallbacks.end(); iter_se ++)
        {
            delete iter_se->second;
        }
	    vcallbacks.clear();
    }


    int ExceptionCapture::operator()(PEXCEPTION_POINTERS ppoint)
    {
        std::map<std::string, ExceptionHandlerBasic*>::iterator iter_se;
        for(iter_se = vcallbacks.begin(); iter_se != vcallbacks.end(); iter_se ++)
        {
            (*iter_se->second)(ppoint);
        }
	    //return 0;
        return EXCEPTION_EXECUTE_HANDLER;
    }

}


#endif

